home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
scheme
/
boxer
/
boxer.lha
/
parse1.lisp
< prev
next >
Wrap
Text File
|
1993-07-17
|
13KB
|
356 lines
;; -*- Mode: LISP; Package: BOXER; Base: 10.; Fonts: CPTFONT -*-
;;; (C) Copyright 1985 Massachusetts Institute of Technology
;;;
;;; Permission to use, copy, modify, distribute, and sell this software
;;; and its documentation for any purpose is hereby granted without fee,
;;; provided that the above copyright notice appear in all copies and that
;;; both that copyright notice and this permission notice appear in
;;; supporting documentation, and that the name of M.I.T. not be used in
;;; advertising or publicity pertaining to distribution of the software
;;; without specific, written prior permission. M.I.T. makes no
;;; representations about the suitability of this software for any
;;; purpose. It is provided "as is" without express or implied warranty.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This file contains the upper-level code for parsing boxes
;;; into LISP code. There are two procedures available to call:
;;;
;;; PARSE-BOX-INTO-LAMBDA takes a BOX as input and returns a
;;; lambda expression representing the box. The arglist of the
;;; lambda will be the arglist of the box.
;;;
;;; PARSE-INTO-CODE takes a BOX, ROW, or list of ROWS as input,
;;; and returns LISP-evalable code.
;;;
;;; PARSE-LIST-INTO-CODE will take a list of elements and parse
;;; it into code.
;;;
;;; This file is responsible for taking those type of inputs and
;;; getting the lowest-level elements of their rows to give to
;;; the Pratt parser found in PARSE2, which does the actual work
;;; of parsing. General parsing and special forms are dealt
;;; with in that file.
;;;
;;; The interface function in that file is PARSE; it takes a
;;; list of symbols, numbers, strings, and boxes and returns an
;;; evalable form which PARSE-INTO-CODE or PARSE-BOX-INTO-LAMBDA
;;; will glom together and wrap in something.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Fixes for things that are broken elsewhere in the old
;;;release.
(DEFMACRO PARSER-BARF (STRING &rest args)
`(FERROR ,STRING . ,args))
(defun parser-typep (object)
(cond ((doit-box? object) ':doit-box)
((data-box? object) ':data-box)
(t (typep object))))
(defun parser-number-of-args (item)
(IF (BOX? ITEM) (LENGTH (PARSER-BOXER-ARGLIST ITEM))
(ldb %%arg-desc-min-args (boxer-args-info item))))
(defun entries-on-input-row (box)
"Returns the entries on the input row of the box, or nil of none."
(let ((1row-entries (ROW-ENTRIES (tell box :row-at-row-no 0))))
(IF (memq (car 1row-entries) '(bu:INPUT bu:INPUTS bu:))
(cdr 1row-entries)
NIL)))
(DEFUN PARSER-BOXER-ARGLIST (BOX)
"The BOXER-ARGLIST function calls the parser, so we have to have
our own function for getting the arglist out of a doit box.
This function should return the toplevel arglist, without any
destructured variables."
(check-arg-type box doit-box "a DOIT box")
(mapcar #'(LAMBDA (entry)
(if (label-pair? entry)
(label-pair-label entry)
entry))
(entries-on-input-row box)))
;;;Returns the special arglist for destructuring. It is a list
;;;which has one item for each arg in the real arglist of the
;;;a lambda for this box. The car of each of these items is the name
;;;of the lisp input, as found in the bvl of the lambda.
;;;PARSER-BOXER-ARGLIST returns a list of these CARs (i.e., the
;;;lisp arglist).
;;;Structure of the elements of the list: After the lisp name of
;;;the variable comes any number of lists, one for each row in
;;;the destructuring box. Each list contains one or more items,
;;;which (as now implemented) are the names the corresponding parts
;;;of the input should be bound to.
(DEFUN PARSER-BOXER-ARGLIST-FOR-DESTRUCTURING (BOX)
(check-arg-type box doit-box "a DOIT box")
(parser-destructured-args
(entries-on-input-row box)))
(defun parser-destructured-args (entry)
(cond ((symbolp entry) entry)
((label-pair? entry)
(cons (label-pair-label entry)
(parser-destructured-args (label-pair-element entry))))
((listp entry)
(mapcar #'parser-destructured-args entry))
((data-box? entry)
(remq nil
(mapcar #'(lambda (row)
(parser-destructured-args
(row-entries row)))
(box-rows entry))))
(t (parser-barf "~S -- not recognized input object" entry))))
;;;Flattens out a list. When called on a destructuring arglist, returns
;;;a list of all the variables involved.
(defun flatten-list (list)
(cond ((null list) nil)
((atom (car list))
(cons (car list)
(flatten-list (cdr list))))
(t (nconc (flatten-list (car list))
(flatten-list (cdr list))))))
;;;Given a BOX, return a lambda expression representing the box.
;;;The arglist of the lambda is the arglist of the box. Any
;;;destructuring is done by the destructuring code in the lambda.
;;;The rest of the lambda body is constructed of all the rows of
;;;the box run through PARSE-ROW-INTO-CODE.
;;;PARSE-ROW-INTO-CODE is given (in addition to the row) a list
;;;of variables (probably not yet bound) to be considered bound
;;;to data objects. Note that all the destructured variables
;;;must be included in this list. The order doesn't matter:
;;;it's just so PARSE-ROW-INTO-CODE will understand them when it
;;;comes to them.
;;;Once we allow functions as arguments the variable must be
;;;declared to be a function in the arglist, so we can pass that
;;;information along to parse-row-into-code also.
(defun parse-box-into-lambda (box)
(check-arg-type box doit-box "a DOIT box")
(let* ((INPUTS-FOR-LAMBDA (mapcar #'(lambda (input)
(if (box? input) ;destructured
(gensym) ;but without a name.
input)) ;this doesn't work right.
(parser-boxer-arglist box)))
(rows (if (null inputs-for-lambda)
(box-rows box)
(cdr (box-rows box))))
(DESTRUCTURED-ARGUMENTS-LIST
(parser-boxer-arglist-for-destructuring box))
; (local-definitions (find-local-definitions rows))
; (local-procedures (car local-definitions))
; (local-variables (cadr local-definitions))
(arglist-variables (flatten-list destructured-arguments-list))
(BODY
(delq nil (mapcar #'(LAMBDA (row)
(PARSE-ROW-INTO-CODE
ROW
NIL
NIL
;local-variables
;local-procedures
arglist-variables))
rows))))
(cond ((null body) `(LAMBDA () ',INPUTS-FOR-LAMBDA NIL))
((some destructured-arguments-list #'listp) ;Any destructuring?
`(LAMBDA ()
',inputs-for-lambda ;just for show
(*CATCH 'STOP-EXECUTING-THIS-BOX
(bind-destructure-arguments
,inputs-for-lambda
,(parser-boxer-arglist-for-destructuring box)
.,body))))
(t
`(LAMBDA ()
',INPUTS-FOR-LAMBDA ;just for show
(*CATCH 'STOP-EXECUTING-THIS-BOX
.,body))))))
;This needs to use with-boxer-bindings rather than let*.
(defmacro bind-destructure-arguments (lambda-list destr-list &body body)
(let ((gensym-value-list (mapcar #'(lambda (ignore) (gensym)) lambda-list)))
`(let (,@(mapcar #'(lambda (gensym-value-name value)
`(,gensym-value-name (box-items-list (boxer-symeval ',value))))
gensym-value-list
lambda-list))
(boxer-let* ,(binding-list destr-list gensym-value-list)
.,body))))
;generates a binding list given a list of destructuring patterns
;and the gensymmed variables containing the lists with the values.
(defun binding-list (description-list gensym-list)
(apply #'append ;crock
(mapcar #'(lambda (description gensym-containing-value)
(binding-list-1 (cdr description)
gensym-containing-value))
description-list
gensym-list)))
;path is initially a gensymmed variable name containig a list of values
;to fit the desription, but it has cars and cdrs prepended to it.
(defun binding-list-1 (description path)
(if (null description) nil
(append
(binding-list-2 (car description) (list 'car-not-nil path))
(binding-list-1 (cdr description) (list 'cdr-not-nil path)))))
(defun binding-list-2 (description path)
(if (null description) nil
(cons (list (car description) `(car-not-nil ,path))
(binding-list-2 (cdr description) (list 'cdr-not-nil path)))))
(defun car-not-nil (arg)
(if (not (null arg)) (car arg)
(parser-barf "Some argument to the current function is a destructured box ~
with the wrong number of elements.")))
(defun cdr-not-nil (arg)
(if (not (null arg)) (cdr arg)
(parser-barf "Some argument to the current function is a destructured box ~
with the wrong number of elements.")))
;bind-destructure-arguments is a hairy macro that converts this:
;(bind-destructuring-arguments
; (part1 part2)
; ((part1 (a b) (c d))
; (part2 (x y z)))
; (boxer-funcall bu:mumble a b x y z))
;into something like this:
;(let ((part1-list (box-items part1))
; (part2-list (box-items part2)))
; (let ((a (car (car part1-list)))
; (b (cadr (car part1-list)))
; (c (car (cadr part1-list)))
; (d (cadr (cadr part1-list)))
; (x (car (car part2-list)))
; (y (cadr (car part2-list)))
; (z (caddr (car part2-list))))
; (boxer-funcall bu:mumble a b x y z)))
;except part1-list and part2-list are GENSYMS.
(defun box-items-list (box)
(check-arg-type box data-box "a data box")
(mapcar #'row-entries
(box-rows box)))
;;; This takes a ROW and returns what it parses into. The
;result should be object that EVAL will like. Since we
;aren't parsing a box, there's no lambda-list to worry about.
;Any definitions encountered should be done.
(DEFUN parse-into-code (stuff)
(cond ((or (listp stuff) (null stuff))
(parse-rows-as-code stuff))
((row? stuff) (parse (tell stuff :ENTRIES)))
((box? stuff) `(BOXER-FUNCALL ,(list 'QUOTE stuff)))
((or (numberp stuff) (stringp stuff)) stuff)
(T
(parser-BARF "~s cannot be parsed" STUFF))))
;;; Takes a list of rows and returns a PROGN. Again, no variables
;;; that aren't bound need be considered.
(DEFUN PARSE-ROWS-AS-CODE (ROWS)
`(PROGN .,(MAPCAR #'parse-row-into-code rows)))
(DEFUN PARSE-ROW-INTO-CODE (ROW &REST ARGS)
(LEXPR-FUNCALL #'PARSE (TELL ROW :ENTRIES) ARGS))
(deff parse-list-into-code 'parse)
;Returns two values: procedures and variables defined with in
;the box. Things must be defined as first thing on the line.
;Probably some problem with label-pairs. FOO:BARbaz.
;Simplifying assumption:
;If the object following the is a DOIT-BOX, then it's a procedure,
;otherwise it's a variable.
;Returns a list of procedures (car) and variables (cadr).
;Each procedure is a list of the name, the doit box, and the data type.
;Each variable is a list of the name and the value.
(DEFMACRO PARSER-PROCEDURE-SYMBOL-DESCRIPTOR-NUMBER-OF-ARGS (THING)
`(CADDR ,THING))
(DEFMACRO PARSER-PROCEDURE-SYMBOL-DESCRIPTOR-VALUE (THING)
`(CADR ,THING))
(DEFMACRO PARSER-PROCEDURE-SYMBOL-DESCRIPTOR-NAME (THING)
`(CAR ,THING))
(DEFMACRO MAKE-PARSER-PROCEDURE-SYMBOL-DESCRIPTOR (NAME VALUE NARGS)
`(LIST ,NAME ,VALUE ,NARGS))
;(defun find-local-definitions (box-rowlist)
; (loop for row in box-rowlist
; for entry = (car (row-entries row))
; when (name-pair? entry)
; when (doit-box? (name-pair-element entry))
; collect (MAKE-PARSER-PROCEDURE-SYMBOL-DESCRIPTOR
; (name-pair-name entry)
; (name-pair-element entry)
; (parser-number-of-args (name-pair-element entry)))
; into procedures
; else collect (list (name-pair-name entry)
; (name-pair-element entry))
; into variables
; finally
; (return (list procedures variables))))
;Given a box, this function goes through and executes all the "" definitions
;in the box, and all its sub-boxes. It's for use right after READ, etc.
;Note that map-over-all-inferior-boxes doesn't do the current-box...
;(defun process-box-local-definitions (box)
; (check-box-arg box)
; (let ((*currently-executing-box* nil) ;Let this happen as if it were done
; (*boxer-binding-alist-root* nil)) ;at toplevel inside each box so it will
; ;side effect the boxes.
; (process-one-boxes-local-definitions
; box)
; (map-over-all-inferior-boxes
; box
; 'process-one-boxes-local-definitions)))
(COMPILER:MAKE-OBSOLETE process-box-local-definitions "It was used for handling 's")
;(defun process-one-boxes-local-definitions (box)
; (let ((*boxer-static-variables-root* box))
; (mapc #'(lambda (row)
; (if (row-contains-character? row *naming-code*)
; (let ((entry (car (row-entries row))))
; (cond ((name-pair? entry)
; (boxer-make (name-pair-name entry)
; (name-pair-element entry))
; (if (box? (name-pair-element entry))
; (tell (name-pair-element entry)
; :set-name
; (name-pair-name entry))))))))
; (box-rows box))))
(COMPILER:MAKE-OBSOLETE process-one-boxes-local-definitions "It was used for handling 's")
;temporary -- move to emanip
(defun row-contains-character? (row character)
(let* ((array (tell row :chas-array))
(length (array-active-length array)))
(do* ((i 0 (1+ i)))
((= i length) nil)
(if (eq character (cha-code (aref array i)))
(return t)))))